﻿// ReSharper disable CSharpWarnings::CS0197
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Generic;
using Xtro.DirectX;
using Xtro.Gerecler;
using Xtro.DirectX.Direct3D;
using Xtro.MDX.DXGI;
using Xtro.MDX.Direct3D10;
using Xtro.MDX.Direct3DX10;
using Xtro.MDX.Generic;
using Buffer = Xtro.MDX.Direct3D10.Buffer;
using Color = Xtro.MDX.Direct3DX10.Color;
using D3DX10Functions = Xtro.MDX.Direct3DX10.Functions;
using D3DX10Constants = Xtro.MDX.Direct3DX10.Constants;
using Device = Xtro.MDX.Direct3D10.Device;
using Error = Xtro.MDX.DXGI.Error;
using Message = System.Windows.Forms.Message;
using MessageBox = Xtro.Gerecler.MessageBox;

namespace XtroDirectXDemo
{
    // ReSharper disable InconsistentNaming
    sealed partial class FormMain : Form
    // ReSharper restore InconsistentNaming
    {
        struct SColor
        {
            // ReSharper disable NotAccessedField.Local
            public byte R;
            public byte G;
            public byte B;
            public byte A;
            // ReSharper restore NotAccessedField.Local
        }

        struct SSimpleVertex
        {
            // ReSharper disable NotAccessedField.Local
            public Vector3 Position;
            public Color Color;
            // ReSharper restore NotAccessedField.Local
        }

        readonly uint VertexSize = (uint)Marshal.SizeOf(typeof(SSimpleVertex));

        internal FormSetup FormSetup;

        internal TContext Context;
        internal TScreen ScreenPanel1;
        internal TScreen ScreenPanel2;
        internal TScreen ScreenWindow;

        Viewport Viewport = new Viewport
        {
            Width = 1,
            Height = 1,
            MinimumDepth = 0,
            MaximumDepth = 1f,
            TopLeftX = 0,
            TopLeftY = 0
        };

        Rectangle Scissor = new Rectangle(0, 0, 1, 1);

        RasterizerState RasterizerState;
        Effect Effect;
        EffectTechnique Technique;
        EffectPass Pass;
        InputLayout InputLayout;
        Buffer VertexBuffer;
        Buffer IndexBuffer;
        EffectMatrixVariable WorldVariable;
        EffectMatrixVariable ViewVariable;
        EffectMatrixVariable ProjectionVariable;
        Matrix World;
        Matrix View;
        Matrix Projection;

        FormRenderTarget FormRenderTarget;

        internal FormMain()
        {
            InitializeComponent();
        }

        void Application_Idle(object Sender, EventArgs E)
        {
            try
            {
                if (!CheckBoxLooping.Checked) return;

                try { RenderLiveScreens(); }
                finally { Windows.PostMessage(Handle, 1024/*WM_USER*/, 0, 0); }
            }
            // if Idle event throws an exception, Application can't fire Application_Exception event. So we do it.
            catch (Exception Ex) { Application_Exception(Sender, new ThreadExceptionEventArgs(Ex)); }
        }

        private void RenderLiveScreens()
        {
            if (Context == null || !Context.Active) return;

            var Screens = new List<TScreen>();
            if (CheckBoxLivePanel1.Checked) Screens.Add(ScreenPanel1);
            if (CheckBoxLivePanel2.Checked) Screens.Add(ScreenPanel2);
            if (CheckBoxLiveWindow.Checked) Screens.Add(ScreenWindow);
            var ScreenArray = Screens.ToArray();

            Context.PerformRender(ScreenArray);
        }

        readonly UnmanagedMemory<float> MatrixData = new UnmanagedMemory<float>((uint)Marshal.SizeOf(typeof(Matrix)));
        int[] IndicesArray;

        void Context_Started(TContext Sender, Device Device)
        {
            var Ss = new TTexture("Staging Texture", Sender, CpuAccessFlag.Read | CpuAccessFlag.Write)
                     {
                         Size = new SSize3D(20, 20, 1),
                         Compression = TTexture.NCompression.None,
                     };
            Ss.Start();

            var M = Ss.Map(0, 0, Map.ReadWrite);

            var C = new SColor { R = 255, G = 0, B = 0, A = 255 };
            M.Data.Set(0, M.RowPitch / 4 * 0 + 0, ref C);
            C = new SColor { R = 0, G = 255, B = 0, A = 255 };
            M.Data.Set(0, M.RowPitch / 4 * 1 + 1, ref C);
            C = new SColor { R = 0, G = 0, B = 255, A = 255 };
            M.Data.Set(0, M.RowPitch / 4 * 2 + 2, ref C);
            C = new SColor { R = 255, G = 0, B = 0, A = 100 };
            M.Data.Set(0, M.RowPitch / 4 * 3 + 3, ref C);
            C = new SColor { R = 0, G = 255, B = 0, A = 100 };
            M.Data.Set(0, M.RowPitch / 4 * 4 + 4, ref C);
            C = new SColor { R = 0, G = 0, B = 255, A = 100 };
            M.Data.Set(0, M.RowPitch / 4 * 5 + 5, ref C);
            C = new SColor { R = 255, G = 0, B = 255, A = 255 };
            M.Data.Set(0, (uint)(M.RowPitch / 4 * (Ss.Size.Height / 2) + Ss.Size.Width / 2), ref C);
            SColor Cc;
            M.Data.Get(0, 0, out Cc);
            M.Data.Get(0, 1, out Cc);
            M.Data.Get(0, 2, out Cc);
            M.Data.Get(0, 3, out Cc);
            M.Data.Get(0, 4, out Cc);
            M.Data.Get(0, 5, out Cc);
            M.Data.Get(0, (uint)(M.RowPitch / 4 * (Ss.Size.Height / 2) + Ss.Size.Width / 2), out Cc);

            Ss.UnmapAll();
            Ss.SaveToFile("S.dds", ImageFileFormat.DDS);
            Ss.SaveToFile("S.png", ImageFileFormat.PNG);

            try
            {
                if (T1 != null)
                {
                    //T1.ArrayLength = 4;
                    //T1.MipLevels = 0;
                    T1.Compression = TTexture.NCompression.None;
                    T1.Size = new SSize3D(400, 400, 3);
                    T1.Type = TTexture.NType.Volume;
                    T1.Start();
                    //T1.Start(new TTexture.TImageFileInfo
                    //         {
                    //             FileName = "ev.jpg",
                    //             FilterFlags = FilterFlag.Linear,
                    //             MipFilterFlags = FilterFlag.Linear
                    //         });
                    //var LoadInfo = new TextureLoadInfo
                    //               {
                    //                   DestinationBox = new Box { Right = 200, Bottom = 200 },
                    //                   Filter = FilterFlag.Linear,
                    //                   NumberOfElements = 1,
                    //                   NumberOfMips = 1,
                    //                   SourceBox = new Box { Right = (uint)Ss.Size.Width, Bottom = (uint)Ss.Size.Height }
                    //               };
                    //T1.LoadFromTexture(Ss, ref LoadInfo);

                    //var Blobs = T1.SaveToMemoryFile(ImageFileFormat.DDS);

                    //var TextureEv = new TTexture("tev", Context)
                    //                {
                    //                    MipLevels = 0,
                    //                    //Compression = TTexture.NCompression.None,
                    //                    //Type = TTexture.NType.Line
                    //                };
                    //TextureEv.Start(new TTexture.TImageMemoryFileInfo
                    //                {
                    //                    MemoryFile = new UnmanagedMemory(Blobs[0].GetBufferPointer(), Blobs[0].GetBufferSize()),
                    //                    //FileName = "t1d.dds",
                    //                    //FileName = "ev.jpg",
                    //                    FilterFlags = FilterFlag.Linear
                    //                });
                    //Blobs[0].Dispose();

                    //var TextureFlag = new TTexture("tflag", Context);
                    //TextureFlag.Start(new TTexture.TImageFileInfo
                    //                  {
                    //                      FileName = "flag.jpg",
                    //                      FilterFlags = FilterFlag.Linear
                    //                  });

                    //var LoadInfo = new TextureLoadInfo
                    //{
                    //    SourceBox = new Box { Bottom = (uint)TextureEv.Size.Height, Right = (uint)TextureEv.Size.Width },
                    //    DestinationBox = new Box { Bottom = (uint)T1.Size.Height, Right = (uint)T1.Size.Width },
                    //    NumberOfElements = 1,
                    //    NumberOfMips = 1,
                    //    Filter = FilterFlag.None,
                    //};

                    //T1.LoadFromTexture(TextureEv, ref LoadInfo);
                    //LoadInfo.DestinationFirstElement = 2;
                    //T1.LoadFromTexture(TextureFlag, ref LoadInfo);

                    //LoadInfo.DestinationFirstElement = 1;
                    //LoadInfo.Filter = FilterFlag.None;
                    //T1.LoadFromTexture(TextureEv, ref LoadInfo);
                    //LoadInfo.DestinationFirstElement = 3;
                    //T1.LoadFromTexture(TextureFlag, ref LoadInfo);

                    //T1.GenerateMipMaps(0, FilterFlag.Linear);

                    //TextureEv.SaveToFile("tev.jpg", ImageFileFormat.JPG);
                    //TextureEv.SaveToFile("tev.dds", ImageFileFormat.DDS);
                    //TextureFlag.SaveToFile("flag.dds", ImageFileFormat.DDS);
                    //T1.SaveToFile("hebele.jpg", ImageFileFormat.JPG);
                    //T1.SaveToFile("hebele.dds", ImageFileFormat.DDS);
                }

                var RasterizerDescription = new RasterizerDescription
                {
                    CullMode = CullMode.Back,
                    DepthBias = 0,
                    DepthBiasClamp = 0,
                    SlopeScaledDepthBias = 0,
                    FillMode = FillMode.Solid,
                    AntialiasedLineEnable = false,
                    DepthClipEnable = true,
                    FrontCounterClockwise = false,
                    MultisampleEnable = true,
                    ScissorEnable = true
                };
                var Result = Device.CreateRasterizerState(ref RasterizerDescription, out RasterizerState);
                if (Result != 0) throw new EDirectX("Device.CreateRasterizerState", Result, Name);

                Blob Errors;
                Result = D3DX10Functions.CreateEffectFromFile("Effect.fx", null, null, "fx_4_0", 0, 0, Device, null, out Effect, out Errors);
                if (Result != 0) throw new EDirectX("CreateEffectFromFile", Result, Name);

                Technique = Effect.GetTechniqueByIndex(0);
                Pass = Technique.GetPassByIndex(0);

                PassDescription PassDescription;
                Result = Pass.GetDescription(out PassDescription);
                if (Result != 0) throw new EDirectX("EffectPass.GetDescription", Result, Name);

                WorldVariable = Effect.GetVariableByName("World").AsMatrix();
                ViewVariable = Effect.GetVariableByName("View").AsMatrix();
                ProjectionVariable = Effect.GetVariableByName("Projection").AsMatrix();

                // Initialize the view matrix
                var Eye = new Vector3(-2.0f, 2.0f, -3.0f);
                var At = new Vector3(0.0f, 0.0f, 0.0f);
                var Up = new Vector3(0.0f, 1.0f, 0.0f);
                D3DX10Functions.MatrixLookAtLH(out View, ref Eye, ref At, ref Up);

                var InputElementDescriptions = new[]
                {
                    new InputElementDescription
                    {
                        SemanticName = "POSITION",
                        SemanticIndex = 0,
                        Format = Format.R32G32B32_Float,
                        AlignedByteOffset = 0,
                        InputSlot = 0,
                        InputSlotClass = InputClassification.InputPerVertexData,
                        InstanceDataStepRate = 0
                    },
                    new InputElementDescription
                    {
                        SemanticName = "COLOR",
                        SemanticIndex = 0,
                        Format = Format.R32G32B32A32_Float,
                        AlignedByteOffset = 12,
                        InputSlot = 0,
                        InputSlotClass = InputClassification.InputPerVertexData,
                        InstanceDataStepRate = 0
                    }
                };
                Result = Device.CreateInputLayout(InputElementDescriptions, 2, PassDescription.IA_InputSignature, PassDescription.IA_InputSignature.Size, out InputLayout);
                if (Result != 0) throw new EDirectX("Device.CreateInputLayout", Result, Name);

                var VerticesArray = new[]
                {
                    new SSimpleVertex{Position = new Vector3(-1.0f, 1.0f, -1.0f),Color = new Color(0.0f, 0.0f, 1.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(1.0f, 1.0f, -1.0f),Color = new Color(0.0f, 1.0f, 0.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(1.0f, 1.0f, 1.0f),Color = new Color(0.0f, 1.0f, 1.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(-1.0f, 1.0f, 1.0f),Color = new Color(1.0f, 0.0f, 0.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(-1.0f, -1.0f, -1.0f),Color = new Color(1.0f, 0.0f, 1.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(1.0f, -1.0f, -1.0f),Color = new Color(1.0f, 1.0f, 0.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(1.0f, -1.0f, 1.0f),Color = new Color(1.0f, 1.0f, 1.0f, 1.0f)},
                    new SSimpleVertex{Position = new Vector3(-1.0f, -1.0f, 1.0f),Color = new Color(0.0f, 0.0f, 0.0f, 1.0f)}
                };
                var Vertices = new UnmanagedMemory<SSimpleVertex>(VertexSize * (uint)VerticesArray.Length);
                Vertices.Write(0, VerticesArray);

                var BufferDescription = new BufferDescription
                {
                    ByteWidth = Vertices.Size,
                    Usage = Usage.Default,
                    BindFlags = BindFlag.VertexBuffer,
                    CpuAccessFlags = 0,
                    MiscellaneousFlags = 0
                };

                var SubResourceData = new SubResourceData
                {
                    SystemMemory = Vertices,
                    SystemMemoryPitch = 0,
                    SystemMemorySlicePitch = 0
                };

                Result = Device.CreateBuffer(ref BufferDescription, ref SubResourceData, out VertexBuffer);
                if (Result != 0) throw new EDirectX("Device.CreateBuffer", Result, Name);

                IndicesArray = new[] 
                {
                    3, 1, 0,
                    2, 1, 3,
                    0, 5, 4,
                    1, 5, 0,
                    3, 4, 7,
                    0, 4, 3,
                    1, 6, 5,
                    2, 6, 1,
                    2, 7, 6,
                    3, 7, 2,
                    6, 4, 5,
                    7, 4, 6
                };
                var Indices = new UnmanagedMemory<int>(sizeof(int) * (uint)IndicesArray.Length);
                Indices.Write(IndicesArray);
                SubResourceData = new SubResourceData
                {
                    SystemMemory = Indices,
                    SystemMemoryPitch = 0,
                    SystemMemorySlicePitch = 0
                };
                BufferDescription = new BufferDescription
                {
                    ByteWidth = Indices.Size,
                    Usage = Usage.Default,
                    BindFlags = BindFlag.IndexBuffer,
                    CpuAccessFlags = 0,
                    MiscellaneousFlags = 0
                };

                Result = Device.CreateBuffer(ref BufferDescription, ref SubResourceData, out IndexBuffer);
                if (Result < 0) throw new EDirectX("Device.CreateBuffer", Result, Name);

                if (!CheckBoxLivePanel1.Checked) PanelRenderTarget1.Invalidate();
                if (!CheckBoxLivePanel2.Checked) PanelRenderTarget2.Invalidate();
                if (!CheckBoxLiveWindow.Checked) FormRenderTarget.Invalidate();

                if (ScreenWindow != null && FormSetup.WindowSettings.Fullscreen && !ScreenWindow.SetFullscreenMode(FormSetup.WindowSettings.MonitorNo, FormSetup.WindowSettings.DisplayModeNo, FormSetup.WindowSettings.RefreshRateNo)) throw new EGerecler(ScreenWindow.Name, ScreenWindow.GetType(), "Can't enter fullscreen at the moment.", Name);
            }
            catch
            {
                Sender.Stop();

                throw;
            }

            ButtonStartStop.Text = "Stop (F9)";
        }

        void Context_Stopping(TContext Sender, Device Device)
        {
            if (RasterizerState != null) RasterizerState.Release();
            if (Effect != null) Effect.Release();
            if (VertexBuffer != null) VertexBuffer.Release();
            if (IndexBuffer != null) IndexBuffer.Release();
            if (InputLayout != null) InputLayout.Release();

            if (T1 != null && T1.Active) T1.Stop();

            ButtonStartStop.Text = "Start (F9)";
        }

        private void Context_Render(TContext Sender, List<TScreen> Screens)
        {
            TechniqueDescription TechniqueDescription;
            var Result = Technique.GetDescription(out TechniqueDescription);
            if (Result != 0) throw new EDirectX("EffectTechnique.GetDescription", Result, Name);

            foreach (var Screen in Screens)
            {
                // Initialize the projection matrix
                D3DX10Functions.MatrixPerspectiveFovLH(out Projection, (float)D3DX10Constants.PI * 0.35f, Screen.Size.Width / (float)Screen.Size.Height, 0.1f, 100.0f);

                D3DX10Functions.MatrixIdentity(out World);
                MatrixData.Set(0, ref World);
                Result = WorldVariable.SetMatrix(MatrixData);
                if (Result < 0) throw new EDirectX("EffectMatrixVariable.SetMatrix", Result, Name);

                MatrixData.Set(0, ref View);
                Result = ViewVariable.SetMatrix(MatrixData);
                if (Result < 0) throw new EDirectX("EffectMatrixVariable.SetMatrix", Result, Name);
                MatrixData.Set(0, ref Projection);
                Result = ProjectionVariable.SetMatrix(MatrixData);
                if (Result < 0) throw new EDirectX("EffectMatrixVariable.SetMatrix", Result, Name);

                Sender.Device.IA_SetInputLayout(InputLayout);
                Sender.Device.IA_SetPrimitiveTopology(PrimitiveTopology.TriangleList);
                Sender.Device.IA_SetVertexBuffers(0, 1, new[] { VertexBuffer }, new[] { VertexSize }, new[] { (uint)0 });
                Sender.Device.IA_SetIndexBuffer(IndexBuffer, Format.R32_UInt, 0);

                Viewport.Width = (uint)Screen.Size.Width;
                Viewport.Height = (uint)Screen.Size.Height;
                Sender.Device.RS_SetViewports(1, new[] { Viewport });

                Scissor.Width = Screen.Size.Width - Scissor.Left;
                Scissor.Height = Screen.Size.Height - Scissor.Top;
                Sender.Device.RS_SetScissorRectangles(1, new[] { Scissor });

                Sender.Device.RS_SetState(RasterizerState);

                Screen.RenderTarget.SetOutputTarget();

                for (var I = 0; I < TechniqueDescription.Passes; I++)
                {
                    Result = Pass.Apply(0);
                    if (Result != 0) throw new EDirectX("EffectPass.Apply", Result, Name);
                    Sender.Device.DrawIndexed((uint)IndicesArray.Length, 0, 0);

                    Matrix Translation;
                    D3DX10Functions.MatrixTranslation(out Translation, 1,0.1f,1);
                    World *= Translation;
                    MatrixData.Set(0, ref World);
                    Result = WorldVariable.SetMatrix(MatrixData);
                    if (Result < 0) throw new EDirectX("EffectMatrixVariable.SetMatrix", Result, Name);

                    Result = Pass.Apply(0);
                    if (Result != 0) throw new EDirectX("EffectPass.Apply", Result, Name);
                    Sender.Device.DrawIndexed((uint)IndicesArray.Length, 0, 0);
                }
            }

            D3DX10Functions.MatrixPerspectiveFovLH(out Projection, (float)D3DX10Constants.PI * 0.35f, T1.Size.Width / (float)T1.Size.Height, 0.1f, 100.0f);

            Viewport.Width = (uint)T1.Size.Width;
            Viewport.Height = (uint)T1.Size.Height;
            Sender.Device.RS_SetViewports(1, new[] { Viewport });

            Scissor.Width = T1.Size.Width - Scissor.Left;
            Scissor.Height = T1.Size.Height - Scissor.Top;
            Sender.Device.RS_SetScissorRectangles(1, new[] { Scissor });

            T1.SetOutputTarget();

            for (var I = 0; I < TechniqueDescription.Passes; I++)
            {
                Result = Pass.Apply(0);
                if (Result != 0) throw new EDirectX("EffectPass.Apply", Result, Name);
                Sender.Device.DrawIndexed((uint)IndicesArray.Length, 0, 0);
            }
        }

        void Application_Exception(object Sender, ThreadExceptionEventArgs E)
        {
            if (E.Exception is EDirectX && ((EDirectX)E.Exception).Reason.Contains(Error.DeviceRemoved.ToString()))
            {
                Context.Stop();
                MessageBox.Show("Direct3D device was removed or had a serious error.\nYou can click the start button or restart the application.", "Error", 0);
                return;
            }

            // Show error box.

            var OldLooping = CheckBoxLooping.Checked;
            CheckBoxLooping.Checked = false;
            try
            {
                var WindowedHere = false;
                uint MonitorNo = 0, DisplayModeNo = 0, RefreshRateNo = 0;
                if (Context != null && Context.Active && ScreenWindow != null && ScreenWindow.GetFullscreenMode(out MonitorNo, out DisplayModeNo, out RefreshRateNo))
                {
                    try { WindowedHere = ScreenWindow.SetWindowedMode(); }
                    catch { }
                }

                if (ErrorBox.Show(E.Exception, Sender != null))
                {
                    Close();
                    return;
                }

                if (WindowedHere)
                {
                    try { ScreenWindow.SetFullscreenMode(MonitorNo, DisplayModeNo, RefreshRateNo); }
                    catch { }
                }
            }
            finally { CheckBoxLooping.Checked = OldLooping; }
        }

        private void FormMain_Shown(object Sender, EventArgs E)
        {
            try
            {
                Text = Application.ProductName;

                ErrorBox.LogFileName = "Error.log";
                Application.ThreadException += Application_Exception;
                Application.Idle += Application_Idle;

                FormSetup = new FormSetup(this) { Owner = this };

                FormRenderTarget = new FormRenderTarget(this);
                FormRenderTarget.Show();

                Focus();

                InitStage();

                // Setup

                FormSetup.FillAdapters();
                FormSetup.RadioButtonRenderTarget3.Checked = true;

                if (FormSetup.ShowDialog(this) == DialogResult.Cancel)
                {
                    Close();
                    return;
                }

                Setup();

                if (FormSetup.CheckBoxAutoStart.Checked) Context.Start();

                FormSetup.CheckBoxAutoStart.Visible = false;
            }
            // if Shown event throws an exception, Application_Exception event gets innermost exception as parameter. So we call it for outermost exception
            catch (Exception Ex) { Application_Exception(Sender, new ThreadExceptionEventArgs(Ex)); }
        }

        TTexture T1;

        private void InitStage()
        {
            Manager.Start();

            Context = new TContext("Demo Context");

            Context.Started += Context_Started;
            Context.Stopping += Context_Stopping;
            Context.Render += Context_Render;

            CreateScreenPanel1();
            CreateScreenPanel2();
            CreateScreenWindow();

            T1 = new TTexture("Deneme Texture", Context);
        }

        private void Setup()
        {
            // Context

            var StoppedHere = false;
            if (Context.Active)
            {
                if (Context.AdapterNo != FormSetup.ComboBoxAdapter.SelectedIndex || ((Context.Flags & CreateDeviceFlag.Debug) > 0) != FormSetup.CheckBoxDebugLayer.Checked)
                {
                    Context.Stop();
                    StoppedHere = true;
                }
            }

            if (!Context.Active)
            {
                Context.AdapterNo = FormSetup.ComboBoxAdapter.SelectedIndex;

                if (FormSetup.CheckBoxDebugLayer.Checked) Context.Flags |= CreateDeviceFlag.Debug;
                else Context.Flags &= ~CreateDeviceFlag.Debug;
            }

            // ScreenPanel1

            if (ScreenPanel1 == null && FormSetup.CheckBoxRenderTarget1.Checked) CreateScreenPanel1();
            else if (ScreenPanel1 != null && !FormSetup.CheckBoxRenderTarget1.Checked)
            {
                ScreenPanel1.Delete();
                ScreenPanel1 = null;
            }

            if (ScreenPanel1 != null)
            {
                if (!ScreenPanel1.SetSampling(FormSetup.Panel1Settings.AntialiasingNo)) throw new EGerecler(ScreenPanel1.Name, ScreenPanel1.GetType(), "Can't set sampling at the moment.", Name);
                ScreenPanel1.VerticalSync = FormSetup.CheckBoxVerticalSync.Checked;
            }

            // ScreenPanel2

            if (ScreenPanel2 == null && FormSetup.CheckBoxRenderTarget2.Checked) CreateScreenPanel2();
            else if (ScreenPanel2 != null && !FormSetup.CheckBoxRenderTarget2.Checked)
            {
                ScreenPanel2.Delete();
                ScreenPanel2 = null;
            }

            if (ScreenPanel2 != null)
            {
                if (!ScreenPanel2.SetSampling(FormSetup.Panel2Settings.AntialiasingNo)) throw new EGerecler(ScreenPanel2.Name, ScreenPanel2.GetType(), "Can't set sampling at the moment.", Name);
                ScreenPanel2.VerticalSync = FormSetup.CheckBoxVerticalSync.Checked;
            }

            // ScreenWindow

            if (ScreenWindow == null && FormSetup.CheckBoxRenderTarget3.Checked) CreateScreenWindow();
            else if (ScreenWindow != null && !FormSetup.CheckBoxRenderTarget3.Checked)
            {
                ScreenWindow.Delete();
                ScreenWindow = null;
            }

            if (ScreenWindow != null)
            {
                if (!ScreenWindow.SetSampling(FormSetup.WindowSettings.AntialiasingNo)) throw new EGerecler(ScreenWindow.Name, ScreenWindow.GetType(), "Can't set sampling at the moment.", Name);
                ScreenWindow.VerticalSync = FormSetup.CheckBoxVerticalSync.Checked;
            }

            // Context Start

            if (StoppedHere) Context.Start();
            if (!Context.Active) return;

            if (!CheckBoxLivePanel1.Checked) PanelRenderTarget1.Invalidate();
            if (!CheckBoxLivePanel2.Checked) PanelRenderTarget2.Invalidate();
            if (!CheckBoxLiveWindow.Checked) FormRenderTarget.Invalidate();

            if (ScreenWindow != null && FormSetup.WindowSettings.Fullscreen && !ScreenWindow.GetIsFullscreen() && !ScreenWindow.SetFullscreenMode(FormSetup.WindowSettings.MonitorNo, FormSetup.WindowSettings.DisplayModeNo, FormSetup.WindowSettings.RefreshRateNo)) throw new EGerecler(ScreenWindow.Name, ScreenWindow.GetType(), "Can't enter fullscreen at the moment.", Name);
        }

        void CreateScreenWindow()
        {
            ScreenWindow = new TScreen("Window Screen", Context, FormRenderTarget, Size.Empty, FormSetup.WindowSettings.AntialiasingNo)
            {
                InfoOptions =
                {
                    Extra = "Esc : Exit\nF1 : Toggle info\nF2 : Setup",
                    Visible = true,
                    UseOwnViewportAndScissorRectangle = false
                }
            };
            ScreenWindow.EnteringFullscreen += ScreenWindow_EnteringFullscreen;
            ScreenWindow.ExitFullscreen += ScreenWindow_ExitFullscreen;

            if (!CheckBoxAutoResize.Checked) ScreenWindow.SetSize(FormRenderTarget.ClientSize);
        }

        static void ScreenWindow_EnteringFullscreen(TScreen Sender, bool WasInFullscreenAlready, uint MonitorNo, uint DisplayModeNo, uint RefreshRateNo, ModeDescription ModeDescription)
        {
            // do something
        }

        static void ScreenWindow_ExitFullscreen(TScreen Sender)
        {
            // do something
        }

        void CreateScreenPanel1()
        {
            ScreenPanel1 = new TScreen("Screen 1", Context, PanelRenderTarget1, Size.Empty, FormSetup.Panel1Settings.AntialiasingNo)
            {
                InfoOptions =
                   {
                       Extra = "Esc : Exit\nF1 : Toggle info\nF2 : Setup",
                       Visible = true,
                       UseOwnViewportAndScissorRectangle = false
                   }
            };

            if (!CheckBoxAutoResize.Checked) ScreenPanel1.SetSize(PanelRenderTarget1.ClientSize);
        }

        void CreateScreenPanel2()
        {
            ScreenPanel2 = new TScreen("Screen 2", Context, PanelRenderTarget2, Size.Empty, FormSetup.Panel2Settings.AntialiasingNo)
                           {
                               InfoOptions =
                                {
                                    Extra = "Esc : Exit\nF1 : Toggle info\nF2 : Setup",
                                    Visible = true,
                                    UseOwnViewportAndScissorRectangle = false
                                }
                           };

            if (!CheckBoxAutoResize.Checked) ScreenPanel2.SetSize(PanelRenderTarget2.ClientSize);
        }

        private void FormMain_FormClosing(object Sender, FormClosingEventArgs E)
        {
            try
            {
                if (Manager.Active) Manager.Stop();

                Application.ThreadException -= Application_Exception;
                Application.Idle -= Application_Idle;
            }
            // if FormClosing event throws an exception, Closing gets canceled. So we call Application_Exception.
            catch (Exception Ex) { Application_Exception(null, new ThreadExceptionEventArgs(Ex)); }
        }

        protected override bool ProcessKeyEventArgs(ref Message Message)
        {
            if ((Keys)Message.WParam == Keys.PrintScreen) ScreenShot();

            return base.ProcessKeyEventArgs(ref Message);
        }

        internal void FormMain_KeyDown(object Sender, KeyEventArgs E)
        {
            if (E.Shift || E.Alt || E.Control) return;

            switch (E.KeyCode)
            {
            case Keys.Escape:
                Close();
                break;
            case Keys.F1:
                if (ScreenPanel1 != null) ScreenPanel1.InfoOptions.Visible = !ScreenPanel1.InfoOptions.Visible;
                if (ScreenPanel2 != null) ScreenPanel2.InfoOptions.Visible = !ScreenPanel2.InfoOptions.Visible;
                if (ScreenWindow != null) ScreenWindow.InfoOptions.Visible = !ScreenWindow.InfoOptions.Visible;
                break;
            case Keys.F2:
                var WindowedHere = false;
                uint MonitorNo = 0, DisplayModeNo = 0, RefreshRateNo = 0;
                if (Context.Active && ScreenWindow != null && ScreenWindow.GetFullscreenMode(out MonitorNo, out DisplayModeNo, out RefreshRateNo)) WindowedHere = ScreenWindow.SetWindowedMode();

                if (FormSetup.ShowDialog(this) == DialogResult.OK) Setup();
                else // Cancel
                {
                    if (WindowedHere) ScreenWindow.SetFullscreenMode(MonitorNo, DisplayModeNo, RefreshRateNo);
                }

                break;
            case Keys.F9:
                ButtonStartStop.PerformClick();
                break;
            }
        }

        internal void ScreenShot()
        {
            if (Context == null || !Context.Active) return;

            var Now = DateTime.Now;
            if (ScreenPanel1 != null) ScreenPanel1.ScreenShot(ScreenPanel1.Name + "-" + Now.ToString("yyyyMMdd-HHMMss") + ".png", ImageFileFormat.PNG);
            if (ScreenPanel2 != null) ScreenPanel2.ScreenShot(ScreenPanel2.Name + "-" + Now.ToString("yyyyMMdd-HHMMss") + ".png", ImageFileFormat.PNG);
            if (ScreenWindow != null) ScreenWindow.ScreenShot(ScreenWindow.Name + "-" + Now.ToString("yyyyMMdd-HHMMss") + ".png", ImageFileFormat.PNG);

            T1.SaveToFile("vol.dds", ImageFileFormat.DDS);
        }

        private void ButtonStartStop_Click(object Sender, EventArgs E)
        {
            if (Context.Active) Context.Stop();
            else Context.Start();
        }

        private void TimerRun_Tick(object Sender, EventArgs E)
        {
            if (!CheckBoxLooping.Checked) return;

            RenderLiveScreens();
        }

        private void ButtonDisableAll_Click(object Sender, EventArgs E)
        {
            CheckBoxLivePanel1.Checked = false;
            CheckBoxLivePanel2.Checked = false;
            CheckBoxLiveWindow.Checked = false;
        }

        private void ButtonEnableAll_Click(object Sender, EventArgs E)
        {
            CheckBoxLivePanel1.Checked = true;
            CheckBoxLivePanel2.Checked = true;
            CheckBoxLiveWindow.Checked = true;
        }

        private void PanelRenderTarget1_Paint(object Sender, PaintEventArgs E)
        {
            if (!CheckBoxLooping.Checked) return;

            if (!CheckBoxLivePanel1.Checked && Context != null && Context.Active && ScreenPanel1 != null) Context.PerformRender(ScreenPanel1);
        }

        private void PanelRenderTarget2_Paint(object Sender, PaintEventArgs E)
        {
            if (!CheckBoxLooping.Checked) return;

            if (!CheckBoxLivePanel2.Checked && Context != null && Context.Active && ScreenPanel2 != null) Context.PerformRender(ScreenPanel2);
        }

        private void PanelRenderTarget1_Resize(object Sender, EventArgs E)
        {
            if (!CheckBoxLivePanel1.Checked) PanelRenderTarget1.Invalidate();
        }

        private void PanelRenderTarget2_Resize(object Sender, EventArgs E)
        {
            if (!CheckBoxLivePanel2.Checked) PanelRenderTarget2.Invalidate();
        }

        private void CheckBoxAutoResize_CheckedChanged(object Sender, EventArgs E)
        {
            if (ScreenPanel1 != null) ScreenPanel1.SetAutoSizing(CheckBoxAutoResize.Checked);
            if (ScreenPanel2 != null) ScreenPanel2.SetAutoSizing(CheckBoxAutoResize.Checked);
            if (ScreenWindow != null) ScreenWindow.SetAutoSizing(CheckBoxAutoResize.Checked);
        }
    }
}
// ReSharper restore CSharpWarnings::CS0197